package logger

import (
	"bytes"
	"gitee.com/zhucheer/orange/utils"
	"reflect"
	"strings"
	"text/template"
	"time"
)

// Message log message struct
type Message struct {
	FormatType    string
	ContentFormat string
	TimeFormat    string
}

// GetMessage, return formatted message string for output.
func (formatter *Message) GetMessage(record *Record) []byte {
	// init format string
	if formatter.TimeFormat == "" {
		formatter.TimeFormat = time.RFC3339
	}
	if formatter.ContentFormat == "" {
		formatter.ContentFormat = "{{.Color}}{{.LevelString}} [{{.Time}}] {{.Message}}{{.KvJson}} {{.Path}}({{.Line}}) {{.ColorClear}}\n"
	}

	logBuffer := new(bytes.Buffer)
	record.Time = time.Now().Format(formatter.TimeFormat)
	if formatter.FormatType == JsonType {
		jsonByte := formatter.GetRecordKvJsonByte(record, record.KvField)
		logBuffer.Write(jsonByte)
	} else {
		record.KvJson = formatter.GetFieldKvJson(record.KvField)
		record.KvJson = strings.TrimRight(record.KvJson, "\n")
		tpl := template.Must(template.New("messageFormat").Parse(formatter.ContentFormat))
		tpl.Execute(logBuffer, record)
	}
	return logBuffer.Bytes()
}

// GetKvJson
func (formatter *Message) GetRecordKvJsonByte(record *Record, fields []Field) (jsonByte []byte) {
	recordMap, _ := jsonStructify(record)
	kvMap := field2Map(fields)

	for k, v := range kvMap {
		if _, ok := recordMap[k]; ok {
			continue
		}
		recordMap[k] = v
	}

	kvJson := utils.JsonEncode(recordMap, false)
	return []byte(kvJson)
}

// GetFieldKvJson
func (formatter *Message) GetFieldKvJson(fields []Field) (kvJson string) {
	if len(fields) == 0 {
		return
	}
	kvMap := field2Map(fields)

	kvJson = utils.JsonEncode(kvMap, false)
	return kvJson
}

// field2Map
func field2Map(i []Field) map[string]interface{} {
	m := make(map[string]interface{})

	for _, item := range i {
		if item.Key == "" {
			continue
		}
		m[item.Key] = item.Value
	}
	return m
}

func jsonStructify(i interface{}) (map[string]interface{}, bool) {
	rType := reflect.TypeOf(i)
	rVal := reflect.ValueOf(i)

	if rType.Kind() != reflect.Struct && rType.Kind() != reflect.Ptr {
		return map[string]interface{}{}, false
	}

	if rType.Kind() == reflect.Ptr && rType.Elem().Kind() != reflect.Struct {
		return map[string]interface{}{}, false
	}

	if rType.Kind() == reflect.Ptr && rType.Elem().Kind() == reflect.Struct {
		rType = rType.Elem()
		rVal = rVal.Elem()
	}

	m := make(map[string]interface{})
	for i := 0; i < rType.NumField(); i++ {
		t := rType.Field(i)
		f := rVal.Field(i)

		tags := strings.Split(t.Tag.Get("json"), ",")
		var tag string
		if len(tags) == 0 || len(tags[0]) == 0 {
			tag = t.Name
		} else if tags[0] == "-" {
			continue
		} else {
			tag = tags[0]
		}
		value := f.Interface()
		m[tag] = value
	}

	return m, true
}
